/**
 * 
 */
package edu.umd.clip.lm.model.decoding;

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicLong;

import edu.umd.clip.lm.model.AbstractProbTree;
import edu.umd.clip.lm.model.Experiment;
import edu.umd.clip.lm.model.HistoryTreePayload;
import edu.umd.clip.lm.model.ProbTree;
import edu.umd.clip.lm.util.BinaryPrefix;
import edu.umd.clip.lm.util.tree.BinaryTree;

class TrellisItem implements AbstractProbTree, Cloneable {
	protected ProbTree tree;
	private double prevProb;
	protected ClusterId clusterid;
	protected TrellisSlice slice;
	private static AtomicLong lastid = new AtomicLong();
	private long id;
	//private LinkQueue queue;
	protected BinaryTree<HistoryTreePayload> decodingPosition[];
	/**
	 * @param tree
	 * @param prevProb
	 * @param clusterid
	 */
	public TrellisItem(ProbTree tree, double prevProb, ClusterId clusterid, BinaryTree<HistoryTreePayload>[] positions) {
		this.tree = tree;
		this.prevProb = prevProb;
		this.clusterid = clusterid;
		this.decodingPosition = positions;
		id = makeId();
	}

	@SuppressWarnings("unchecked")
	public TrellisItem(ProbTree tree, double prevProb, ClusterId clusterid) {
		this(tree, prevProb, clusterid, new BinaryTree[Experiment.getInstance().getNumLMs()]);
	}
	
	public TrellisItem(ProbTree tree, double prevProb) {
		this(tree, prevProb, new ClusterId());
	}		
	
	public BinaryTree<HistoryTreePayload> getPosition(int lmId) {
		return decodingPosition[lmId];
	}
	
	public void setPosition(int lmId, BinaryTree<HistoryTreePayload> position) {
		decodingPosition[lmId] = position;
	}
	
	public void addPrevProb(double prob) {
		prevProb += prob;
	}
	
	public double getProb() {
		return prevProb * tree.getTotalProb();
	}
	
	public double getPrevProb() {
		return prevProb;
	}
	
	public void setPrevProb(double p) {
		prevProb = p;
	}
	
	public void updatePrevProb() {
		ArrayList<TrellisItem> prevs = slice.curr2prev.get(this);
		if (prevs != null) {
			double prevProb = 0;
			for(TrellisItem item : prevs) {
				prevProb += item.prevProb * item.tree.getProbTree().getTotalProb();
			}
			this.prevProb = prevProb;
		}
	}
	
	private static long makeId() {
		return lastid.incrementAndGet();
	}
	
	public String toString() {
		return Long.toString(getId()) + "at" + Integer.toString(slice.getTime());
	}
	
	public long getId() {
		return id;
		//return 0;
	}

	public BinaryPrefix getWordPrefix() {
		return slice.wordPrefix;
	}

	public Boolean testCut(BinaryPrefix cuttingPrefix) {
		return tree.testCut(cuttingPrefix);
	}

	public long getTuple() {
		return slice.overtFactors;
	}

	protected boolean checkPrevProb() {
		return slice.checkPrevProb(this);
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.AbstractProbTree#testOvertFactors(edu.umd.clip.lm.factors.Factors)
	 */
	public Boolean testOvertFactors(long factors) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.AbstractProbTree#cut(edu.umd.clip.lm.util.BinaryPrefix)
	 */
	public AbstractProbTree cut(BinaryPrefix cuttingPrefix) {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.AbstractProbTree#getPrefix()
	 */
	public BinaryPrefix getPrefix() {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.AbstractProbTree#getTotalProb()
	 */
	public double getTotalProb() {
		throw new UnsupportedOperationException();
	}

	public ProbTree getProbTree() {
		return tree.getProbTree();
	}

	@Override
	public Object clone() {
		TrellisItem copy = new TrellisItem((ProbTree)tree.clone(), 0, (ClusterId)clusterid.clone());
		return copy;
	}
	
	public TrellisItem copy() {
		TrellisItem copy = new TrellisItem(tree.getProbTree().copy(), 0, (ClusterId)clusterid.clone());
		return copy;			
	}
	
	protected void makeGhost() {
		// TODO: fix ghosts
		//tree = new GhostProbTree(tree);
		//queue = null;
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.AbstractProbTree#scale(double)
	 */
	public void scale(double scale) {
		this.tree.scale(scale);
	}

	/*
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((clusterid == null) ? 0 : clusterid.hashCode());
		result = prime * result + ((tree == null) ? 0 : tree.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof SliceItem))
			return false;
		SliceItem other = (SliceItem) obj;
		if (clusterid == null) {
			if (other.clusterid != null)
				return false;
		} else if (!clusterid.equals(other.clusterid))
			return false;
		if (tree == null) {
			if (other.tree != null)
				return false;
		} else if (!tree.equals(other.tree))
			return false;
		return true;
	}
	*/
}